﻿using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Crm.SdkTypeProxy;
using System.Net;
using Microsoft.Crm.Sdk;
using Microsoft.Crm.SdkTypeProxy.Metadata;
using Boomerang.Common.Crm.DiscoveryService;
using Boomerang.Common.Enumerations;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;

namespace Boomerang.Common.Crm
{
    public sealed class CrmConnection : IDisposable
    {
        private CrmService service;
        private MetadataService metadataService;
        private CrmDiscoveryService discoveryService;
        private CrmConnectionOptions options;
        private CrmAuthenticationToken token;

        //public CrmConnection(string server, string org, string domain, string userName, string password)
        //{
        //    throw new NotImplementedException();
        //    //NetworkCredential cred = new NetworkCredential();
        //    //cred.Domain = domain;
        //    //cred.UserName = userName;
        //    //cred.Password = password;

        //    //CrmAuthenticationToken token = new CrmAuthenticationToken();
        //    //token.AuthenticationType = 0;
        //    //token.OrganizationName = org;
        //    //service = new CrmService();
        //    //service.Url = server + "/mscrmservices/2007/crmservice.asmx";
        //    //service.CrmAuthenticationTokenValue = token;
        //    //service.Credentials = cred;

        //    //cred = null;
        //}

        public CrmConnection(CrmConnectionOptions options)
        {
            this.options = options;

        }

        public MetadataService MetadataService
        {
            get
            {
                if (metadataService == null)
                    metadataService = GetMetadataService();
                return metadataService;
            }
        }

        public CrmService CrmService
        {
            get
            {
                if (service == null)
                    service = GetCrmService();
                return service;
            }
        }

        public CrmDiscoveryService DiscoveryService
        {
            get
            {
                if (discoveryService == null)
                    discoveryService = GetDiscoveryService();
                return discoveryService;
            }
        }

        private CrmAuthenticationToken Token
        {
            get
            {
                if (token == null)
                {
                    token = new CrmAuthenticationToken();
                    token.AuthenticationType = (int)options.AuthMode;
                    token.OrganizationName = options.OrgName;

                    if (options.AuthMode == CrmConnectionType.IFD)
                        token.CrmTicket = GetCrmTicketSPLA();
                }

                return token;
            }
        }

        private string GetCrmTicketSPLA()
        {
            NetworkCredential credentials = this.options.GetCrmCredentials() as NetworkCredential;
            if (credentials == null)
            {
                throw new ArgumentException("Options.GetCrmCredentials must be set to a valid NetworkCredential object in SPLA mode.");
            }
            if (string.IsNullOrEmpty(this.options.DiscoveryServerUrl))
            {
                throw new ArgumentException("Options.DiscoveryServiceUrl must be set in SPLA mode.");
            }
            CrmDiscoveryService _service = new CrmDiscoveryService();
            _service.Url = this.options.GetCrmDiscoveryServiceUrl();
            RetrieveCrmTicketRequest request = new RetrieveCrmTicketRequest();
            request.OrganizationName = this.options.OrgName;
            request.UserId = this.options.UserName;
            request.Password = this.options.Password;
            RetrieveCrmTicketResponse response = (RetrieveCrmTicketResponse)_service.Execute(request);
            return response.CrmTicket;
        }

        public static List<OrganizationDetail> GetOrganizations(CrmConnectionOptions options)
        {
            RetrieveOrganizationsRequest request = new RetrieveOrganizationsRequest();

            CrmDiscoveryService service = new CrmConnection(options).DiscoveryService;

            if (options.AuthMode != CrmConnectionType.OnPremise)
            {
                request.UserId = options.UserName;
                request.Password = options.Password;
            }

            RetrieveOrganizationsResponse response = (RetrieveOrganizationsResponse)service.Execute(request);

            return new List<OrganizationDetail>(response.OrganizationDetails);
        }

        private static bool TrustAllCertificateCallback(object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors errors)
        {
            return true;
        }

        private CrmDiscoveryService GetDiscoveryService()
        {
            CrmDiscoveryService _service;
            _service = new CrmDiscoveryService();
            _service.Url = options.GetCrmDiscoveryServiceUrl();

            if (options.UseHttps)
            {
                ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(TrustAllCertificateCallback);
            }

            if (options.AuthMode == CrmConnectionType.OnPremise)
            {
                _service.Credentials = options.GetCrmCredentials();

            }

            return _service;
        }

        private CrmService GetCrmService()
        {
            CrmService _service;
            _service = new CrmService();
            _service.Url = options.CrmService;

            if (options.UseHttps)
            {
                ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(TrustAllCertificateCallback);
            }

            _service.Credentials = options.GetCrmCredentials();


            _service.CrmAuthenticationTokenValue = this.Token;

            return _service;
        }

        private MetadataService GetMetadataService()
        {
            MetadataService _service;
            _service = new MetadataService();
            _service.Url = options.MetadataService;

            if (options.UseHttps)
            {
                ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(TrustAllCertificateCallback);
            }

            //if (options.AuthMode == CrmConnectionType.OnPremise)
            //{
            _service.Credentials = options.GetCrmCredentials();
            //}

            _service.CrmAuthenticationTokenValue = this.Token;

            return _service;
        }

        #region IDisposable Members

        public void Dispose()
        {
            if (service != null) service.Dispose();
            if (metadataService != null) metadataService.Dispose();
            if (discoveryService != null) discoveryService.Dispose();
        }

        #endregion

        public bool Test()
        {
            WhoAmIRequest request = new WhoAmIRequest();

            WhoAmIResponse response = (WhoAmIResponse)CrmService.Execute(request);

            if (response.UserId != Guid.Empty)
                return true;
            else
                return false;
        }
    }
}
